#include <mutex>
#include <shared_mutex>

#include "common/config/config.h"
#include "common/elements/element.h"
#include "common/log/log.h"

#include "data_gate.h"

#include "data_elements/data_element_maker.h"

namespace Database
{

DataGate::DataGate(const std::string& databaseFilename)
    : m_database(new sqlite::database(databaseFilename.empty() ? CONFIG.database.file : databaseFilename))
{
}

bool DataGate::saveElement(DataElementPtr dataElement) const
{
    try
    {
        std::unique_lock locker(m_lock);
        dataElement->toDatabase(*m_database);
        return true;
    }
    catch (const sqlite::sqlite_exception& exception)
    {
        LOG_ERROR
            << "Ошибка сохранения элемента: " << exception.what()
            << ", " << exception.get_sql();
    }
    catch (const std::exception& exception)
    {
        LOG_ERROR
            << "Ошибка сохранения элемента: " << exception.what();
    }
    return false;
}

bool DataGate::saveElement(Elements::ElementPtr element) const
{
    try
    {
        std::unique_lock locker(m_lock);
        wrapAnSharedElement(element)->toDatabase(*m_database);
        return true;
    }
    catch (const sqlite::sqlite_exception& exception)
    {
        LOG_ERROR
            << "Ошибка сохранения элемента: " << exception.what()
            << ", " << exception.get_sql();
    }
    catch (const std::exception& exception)
    {
        LOG_ERROR
            << "Ошибка сохранения элемента: " << exception.what();
    }
    return false;
}

DataElementPtr DataGate::loadElement(
    Elements::ElementType elementType,
    unsigned id
) const
{
    try
    {
        std::shared_lock locker(m_lock);
        return fromDatabase(*m_database, elementType, id);
    }
    catch (const sqlite::sqlite_exception& exception)
    {
        LOG_ERROR
            << "Ошибка загрузки элемента: " << exception.what()
            << ", " << exception.get_sql();
    }
    catch (const std::exception& exception)
    {
        LOG_ERROR
            << "Ошибка загрузки элемента: " << exception.what();
    }
    return nullptr;
}

DataElements DataGate::loadElements(
    Elements::ElementType elementType,
    std::vector<unsigned> ids
) const
{
    try
    {
        std::shared_lock locker(m_lock);
        return fromDatabase(*m_database, elementType, ids);
    }
    catch (const sqlite::sqlite_exception& exception)
    {
        LOG_ERROR
            << "Ошибка загрузки элементов: " << exception.what()
            << ", " << exception.get_sql();
    }
    catch (const std::exception& exception)
    {
        LOG_ERROR
            << "Ошибка загрузки элементов: " << exception.what();
    }
    return {};
}

} // namespace Database
